package com.jzo2o.health.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.health.mapper.ReservationSettingMapper;
import com.jzo2o.health.model.domain.ReservationSetting;
import com.jzo2o.health.model.dto.request.ReservationSettingUpsertReqDTO;
import com.jzo2o.health.model.dto.response.ReservationDateResDTO;
import com.jzo2o.health.model.dto.response.ReservationSettingResDTO;
import com.jzo2o.health.service.IReservationSettingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ReservationSettingServiceImpl extends ServiceImpl<ReservationSettingMapper, ReservationSetting> implements IReservationSettingService {
    @Autowired
    private ReservationSettingMapper reservationSettingMapper;
    @Override
    public List<ReservationSettingResDTO> getReservationSettingByMonth(String date) {
        SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-M");
        SimpleDateFormat outputFormat = new SimpleDateFormat("yyyy-MM");
        Date newDate = null;
        try {
            newDate = inputFormat.parse(date);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
        String localDate = outputFormat.format(newDate);
        return reservationSettingMapper.getReservationSettingByMonth(localDate);
    }

    @Override
    public void editNumberByDate(ReservationSettingUpsertReqDTO reservationSettingUpsertReqDTO) {
        ReservationSetting reservationSetting = this.lambdaQuery()
                .eq(ReservationSetting::getOrderDate, reservationSettingUpsertReqDTO.getOrderDate())
                .one();
        if (ObjectUtil.isNotNull(reservationSetting)) {
            if (reservationSettingUpsertReqDTO.getNumber() < reservationSetting.getReservations()) {
                throw new ForbiddenOperationException("预约人数不能小于已预约人数");
            }
            if (reservationSettingUpsertReqDTO.getNumber() == 0 || reservationSettingUpsertReqDTO.getNumber() >= 1000) {
                throw new ForbiddenOperationException("预约人数不正确");
            }
            this.lambdaUpdate()
                    .eq(ReservationSetting::getOrderDate, reservationSettingUpsertReqDTO.getOrderDate())
                    .set(ReservationSetting::getNumber,reservationSettingUpsertReqDTO.getNumber())
                    .update();
            return;
        }
        ReservationSetting setting = BeanUtil.copyProperties(reservationSettingUpsertReqDTO, ReservationSetting.class);
        this.save(setting);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void upload(MultipartFile file) {
        ExcelReader reader = null;
        try {
            reader = ExcelUtil.getReader(file.getInputStream());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        reader.addHeaderAlias("日期\n" + "(实例：2048-10-10）","orderDate");
        reader.addHeaderAlias("可预约数量\n" + "（示例：400；最多999人）","number");
        //读取excel中的记录为ReservationSetting列表
        List<ReservationSetting> excelSettingList = reader.readAll(ReservationSetting.class);

        if (CollUtil.isEmpty(excelSettingList)) {
            throw new ForbiddenOperationException("文件为空");
        }
        //过滤出当前日期之后的记录
        excelSettingList = excelSettingList.stream().filter(e->e.getOrderDate().isAfter(LocalDate.now())).collect(Collectors.toList());
        if (CollUtil.isEmpty(excelSettingList)) {
            throw new ForbiddenOperationException("没有更新数据");
        }
        //获取excel中日期和数量的映射
        Map<LocalDate, Integer> excelDates = excelSettingList.stream()
                .collect(Collectors.toMap(ReservationSetting::getOrderDate, ReservationSetting::getNumber));
        //查询数据库当前日期之后的数据
        List<ReservationSetting> reservationSettingList = this.list(Wrappers.<ReservationSetting>lambdaQuery().gt(ReservationSetting::getOrderDate, LocalDate.now()));
        //如果数据库中当前日期之后的数据不为空
        if (CollUtil.isNotEmpty(reservationSettingList)) {
            //获取数据库中记录的日期集合
            Set<LocalDate> localDateSet = reservationSettingList.stream().map(ReservationSetting::getOrderDate).collect(Collectors.toSet());
            List<ReservationSetting> batchList = new ArrayList<>();
            //过滤出需要更新的记录:excel中的日期等于数据库中查到的日期
            List<ReservationSetting> updateList = reservationSettingList.stream()
                    .filter(e -> excelDates.containsKey(e.getOrderDate()))
                    .map(e->e.setNumber(excelDates.get(e.getOrderDate())))
                    .collect(Collectors.toList());
            if (CollUtil.isNotEmpty(updateList)) {
                batchList.addAll(updateList);
            }
            //过滤出需要新增的数据：不在数据库中的excel中的日期
            List<ReservationSetting> saveList = excelSettingList.stream()
                    .filter(e -> !localDateSet.contains(e.getOrderDate()))
                    .collect(Collectors.toList());
            if (CollUtil.isNotEmpty(saveList)) {
                batchList.addAll(saveList);
            }
            //根据是否存在id新增或修改
            this.saveOrUpdateBatch(batchList);

        } else {
            //如果数据库当前日期之后没有数据，则直接全部新增
            this.saveBatch(excelSettingList);
        }
    }

    @Override
    public ReservationDateResDTO getReservationDateByMonth(String month) {
        List<ReservationSettingResDTO> reservationSettingByMonth = reservationSettingMapper.getReservationSettingByMonth(month);
        List<String> list = reservationSettingByMonth.stream()
                .filter(e -> e.getNumber() > 0)
                .map(ReservationSettingResDTO::getDate)
                .collect(Collectors.toList());
        return new ReservationDateResDTO(list);
    }
}
